import { Component, Node } from 'cc';
/*eslint-disable*/
export type _classExample<T = any> = new (...args: any[]) => T;

// const singleton: Map<string, any> = new Map();
export abstract class Singleton {
  abstract className: string;
  private _instance: any;
  static Ins<T extends Singleton>(this: _classExample<T>): T {
    if ((<any>this)._instance) {
      return (<any>this)._instance;
    } else {
      (<any>this)._instance = new this();
      return (<any>this)._instance;
    }
  }

  destroySingleton() {
    this._instance = null;
  }
}

type PropertyDecorator = ($class: Record<string, any>, $propertyKey: string | symbol, $descriptorOrInitializer?: any) => void;
const checkNode = function (node: Node, nodeName: Set<string>, nodeMap: Map<string, Node>) {
  if (nodeName.has(node.name)) {
    nodeMap.set(node.name, node);
  }
  const childs = node.children;
  const len = childs.length;
  for (let i = 0; i < len; i++) {
    const child = node.children[i];
    if (!child.isValid) continue;
    // if (nodeName.has(child.name)) {
    //     nodeMap.set(child.name, child);
    // }
    checkNode(child, nodeName, nodeMap);
  }
};
export const rootNode = '_root_';
/**
 * 自动绑定节点
 * ps: 如果根节点名字不清楚，规定'_root_'为根节点
 *     绑定单个：绑定第一个匹配到的节点
 *     绑定多个：只会遍历nodeName下的一级子节点，绑定所有匹配到的节点
 * example:
 *     @autoNode(['a','b']) 绑定名字为a、b的节点下所有一级子节点名字前缀为a+数字、b+数字的节点
 *     @autoNode(['a','b'], 'c') 绑定名字为a、b的节点下所有一级子节点名字前缀为c+数字的节点
 *     @autoNode('a') 绑定第一个名字为a的节点
 * @param $nodeName 节点名称
 * @param $regex    正则表达式，匹配节点名称前缀，默认设置前缀为节点名称（$nodeName）
 * @returns
 */
export function autoNode($nodeName?: string[] | string, $regex?: string | RegExp): PropertyDecorator {
  return ($target, $propertyKey: string, $descriptorOrInitializer) => {
    const cache: Map<string, { _nodeName: string | string[]; _regex?: string | RegExp }> = ($target['__ccc_decorator_autoNode__'] ??= new Map<
      string,
      { _nodeName: string; _regex?: string | RegExp }
    >());
    if (cache.has($propertyKey)) {
      throw new Error(`autoNode 装饰器重复绑定属性:${$propertyKey}，class:${$target.name}`);
    } else {
      if (Array.isArray($nodeName)) {
        cache.set($propertyKey, { _nodeName: $nodeName, _regex: $regex });
      } else {
        cache.set($propertyKey, { _nodeName: ($nodeName == '' ? $propertyKey : $nodeName) || $propertyKey, _regex: $regex });
      }
    }
    if (cache.size === 1) {
      const oldonLoad: () => void = $target.onLoad || undefined; //$target.onLoad也可以拿到父类的实现
      $target.onLoad = function () {
        const nodeName: Set<string> = new Set<string>();
        cache.forEach(($vo) => {
          if (Array.isArray($vo._nodeName)) {
            $vo._nodeName.forEach((str) => {
              nodeName.add(str);
            });
          } else {
            nodeName.add($vo._nodeName);
          }
        });
        const nodeMap: Map<string, Node> = new Map<string, Node>();
        nodeMap.set(rootNode, this.node);
        checkNode(this.node, nodeName, nodeMap);
        cache.forEach(($vo, $propertyKey) => {
          if (Array.isArray($vo._nodeName)) {
            $vo._nodeName.forEach((nameS) => {
              const node = nodeMap.get(nameS);
              if (!node) {
                throw new Error(`autoNode 节点${nameS}不存在`);
              }
              let regex;
              if ($vo._regex) {
                regex = typeof $vo._regex == 'string' ? new RegExp('^' + $vo._regex + '[0-9]*') : $vo._regex;
              } else {
                regex = new RegExp('^' + nameS + '[0-9]*');
              }
              const arr = [];
              for (let i = 0; i < node.children.length; i++) {
                const child = node.children[i];
                if (!child.isValid) continue;
                if (regex.test(child.name)) {
                  arr.push(child);
                }
              }
              this[$propertyKey] = arr;
            });
          } else {
            const node = nodeMap.get($vo._nodeName);
            if (!node) {
              throw new Error(`autoNode 节点${$vo._nodeName}不存在`);
            }
            this[$propertyKey] = node;
          }
        });
        oldonLoad && oldonLoad.apply(this);
      };
    }
  };
}

/**
 * 自动绑定脚本
 * ps: 如果根节点名字不清楚，规定'_root_'为根节点
 *     绑定单个：绑定第一个匹配到的节点下的脚本
 *     绑定多个：只会遍历nodeName下的一级子节点，绑定所有匹配到的节点的脚本
 * example:
 *     @autoNode([a, b], 'c') 绑定名字为c的节点下所有一级子节点中名字为a、b的脚本
 *     @autoNode([a, b], 'c', 'd') 绑定名字为c的节点下所有一级子节点中名字前缀为d+数字的节点中名字为a、b的脚本
 *     @autoNode(a) 绑定第一个名字为注释的属性名中名字为a的脚本
 * @param $class    脚本类
 * @param $nodeName 节点名称
 * @param $regex    正则表达式，匹配节点名称前缀，默认设置前缀为节点名称（$nodeName）
 * @returns
 */
export function autoComp($class: _classExample<Component> | _classExample<Component>[], $nodeName?: string, $regex?: string | RegExp): PropertyDecorator {
  return ($target, $propertyKey: string, $descriptorOrInitializer) => {
    const cache: Map<string, { _nodeName: string; _class?: _classExample<Component> | _classExample<Component>[]; _regex?: string | RegExp }> = ($target['__ccc_decorator_autoComp__'] ??= new Map<
      string,
      { $opt?: _classExample<Component> | _classExample<Component>[]; $regex?: string | RegExp }
    >());
    if (cache.has($propertyKey)) {
      throw new Error(`autoComp 装饰器重复绑定属性:${$propertyKey}，class:${$target.name}`);
    } else {
      cache.set($propertyKey, { _nodeName: ($nodeName == '' ? $propertyKey : $nodeName) || $propertyKey, _class: $class, _regex: $regex });
    }
    if (cache.size === 1) {
      const oldonLoad: () => void = $target.onLoad || undefined; //$target.onLoad也可以拿到父类的实现
      $target.onLoad = function () {
        const nodeName: Set<string> = new Set<string>();
        cache.forEach(($vo) => {
          nodeName.add($vo._nodeName);
        });
        const nodeMap: Map<string, Node> = new Map<string, Node>();
        nodeMap.set(rootNode, this.node);
        checkNode(this.node, nodeName, nodeMap);
        cache.forEach(($vo, $propertyKey) => {
          const node = nodeMap.get($vo._nodeName);
          if (Array.isArray($vo._class)) {
            if (!node) {
              throw new Error(`autoComp 节点${$vo._nodeName} ${$vo._class[0].name}不存在`);
            }
            const compclass = $vo._class[0];
            let regex;
            if ($vo._regex) {
              regex = typeof $vo._regex == 'string' ? new RegExp('^' + $vo._regex + '[0-9]*') : $vo._regex;
            } else {
              regex = new RegExp('^' + $vo._nodeName + '[0-9]*');
            }
            const arr = [];
            const j = 0;
            for (let i = 0; i < node.children.length; i++) {
              const child = node.children[i];
              if (!child.isValid) continue;
              if (regex.test(child.name)) {
                arr.push(child.getComponent(compclass));
              }
            }
            this[$propertyKey] = arr;
          } else {
            if (!node) {
              throw new Error(`autoComp 节点${$vo._nodeName}不存在 绑定组件：${$vo._class.name}`);
            }
            const compp = node.getComponent($vo._class);
            if (compp) {
              this[$propertyKey] = node.getComponent($vo._class);
            } else {
              console.warn(`autoComp 节点${$vo._nodeName} 绑定组件：${$vo._class.name}不存在，自动添加`);
              this[$propertyKey] = node.addComponent($vo._class);
            }
          }
        });
        oldonLoad && oldonLoad.apply(this);
      };
    }
  };
}
